#include "acl_stdafx.hpp"
#ifndef ACL_PREPARE_COMPILE
#include "stdlib/snprintf.hpp"
#include "stdlib/log.hpp"
#include "stdlib/util.hpp"
#include "stream/socket_stream.hpp"
#include "stream/server_socket.hpp"
#endif

#define	SAFE_COPY	ACL_SAFE_STRNCPY

namespace acl {

server_socket::server_socket(int backlog /* = 128 */, bool block /* = true */)
: backlog_(backlog)
, unix_sock_(false)
, fd_(ACL_SOCKET_INVALID)
, fd_local_(ACL_SOCKET_INVALID)
{
	open_flag_ = block ? ACL_BLOCKING : ACL_NON_BLOCKING;
}

server_socket::server_socket(open_flag_t flag, int backlog /* = 128 */)
: backlog_(backlog)
, unix_sock_(false)
, fd_(ACL_SOCKET_INVALID)
, fd_local_(ACL_SOCKET_INVALID)
{
	open_flag_ = 0;
	if (flag & OPEN_FLAG_NONBLOCK)
		open_flag_ |= ACL_NON_BLOCKING;
	else
		open_flag_ |= ACL_BLOCKING;

	if (flag & OPEN_FLAG_REUSEPORT)
		open_flag_ |= ACL_INET_FLAG_REUSEPORT;
}

server_socket::server_socket(ACL_VSTREAM* sstream)
: fd_(ACL_VSTREAM_SOCK(sstream))
, fd_local_(ACL_SOCKET_INVALID)
{
	if (fd_ != ACL_SOCKET_INVALID)
		addr_ = ACL_VSTREAM_LOCAL(sstream);
}

server_socket::server_socket(ACL_SOCKET fd)
: fd_(fd)
, fd_local_(ACL_SOCKET_INVALID)
{
	char buf[512];
	if (fd_ != ACL_SOCKET_INVALID) {
		if (acl_getsockname(fd_, buf, sizeof(buf)) == 0)
			addr_ = buf;
	}
}

server_socket::~server_socket()
{
	if (fd_local_ != ACL_SOCKET_INVALID)
		acl_socket_close(fd_local_);
}

bool server_socket::opened(void) const
{
	return fd_ != ACL_SOCKET_INVALID;
}

bool server_socket::open(const char* addr)
{
	if (fd_ != ACL_SOCKET_INVALID)
	{
		logger_error("listen fd already opened");
		return true;
	}

#ifndef ACL_WINDOWS
	if (strchr(addr, '/') != NULL)
	{
		fd_ = acl_unix_listen(addr, backlog_, open_flag_);
		unix_sock_ = true;
		addr_ = addr;
	}
	else
#endif
		fd_ = acl_inet_listen(addr, backlog_, open_flag_);

	if (fd_ == ACL_SOCKET_INVALID)
	{
		logger_error("listen %s error %s", addr, last_serror());
		unix_sock_ = false;
		addr_ = addr;
		return false;
	}

	// 保留由本类对象产生的句柄，以便于在析构函数中将其关闭
	fd_local_ = fd_;

	if (unix_sock_)
		return true;

	// 之所以再用 getsockname 重新获得一些监听地址，主要是为了应对当输入
	// 的 addr 为 ip:0 的情形，即当给定的地址中的端口为 0 时要求操作系统
	// 自动分配一个端口号
	char buf[512];
	if (acl_getsockname(fd_, buf, sizeof(buf)) < 0)
	{
		logger_error("getsockname error: %s", acl_last_serror());
		addr_ = addr;
	}
	else
		addr_ = buf;
	return true;
}

ACL_SOCKET server_socket::unbind()
{
	ACL_SOCKET sock = fd_local_;
	fd_local_ = ACL_SOCKET_INVALID;
	fd_ = ACL_SOCKET_INVALID;
	return sock;
}

bool server_socket::close()
{
	if (fd_local_ == ACL_SOCKET_INVALID)
		return true;
	bool ret = acl_socket_close(fd_local_) == 0 ? true : false;
	fd_ = ACL_SOCKET_INVALID;
	fd_local_ = ACL_SOCKET_INVALID;
	addr_.clear();
	return ret;
}

socket_stream* server_socket::accept(int timeout /* = 0 */)
{
	if (fd_ == ACL_SOCKET_INVALID)
	{
		logger_error("server socket not opened!");
		return NULL;
	}

	if ((open_flag_ & ACL_NON_BLOCKING) && timeout > 0)
	{
		if (acl_read_wait(fd_, timeout) == -1)
			return NULL;
	}

	ACL_SOCKET fd = acl_accept(fd_, NULL, 0, NULL);
	if (fd == ACL_SOCKET_INVALID)
	{
		if (open_flag_ & ACL_NON_BLOCKING)
			logger_error("accept error %s", last_serror());
		else if (last_error() != ACL_EAGAIN
			&& last_error() != ACL_EWOULDBLOCK)
		{
			logger_error("accept error %s", last_serror());
		}
		return NULL;
	}

	socket_stream* client = new socket_stream();
	if (client->open(fd) == false)
	{
		logger_error("create socket_stream error!");
		return NULL;
	}

	if (!unix_sock_)
		acl_tcp_set_nodelay(fd);

	return client;
}

void server_socket::set_tcp_defer_accept(int timeout)
{
	if (fd_ == ACL_SOCKET_INVALID)
		logger_error("server socket not opened!");
	else
		acl_tcp_defer_accept(fd_, timeout);
}

} // namespace acl
